package collectors.file.wrapper;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

import collectors.file.models.FileInfo;
import play.Logger;
import play.Play;
import play.libs.Akka;
import play.libs.F.Option;
import play.libs.F.Promise;
import play.mvc.Controller;
import play.mvc.Result;
import services.CollectorService;
import services.CollectorService.Services;
import collectors.file.models.FileUser;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.RESTUtility;
import com.dropbox.client2.exception.DropboxException;
import com.dropbox.client2.session.AccessTokenPair;
import com.dropbox.client2.session.AppKeyPair;
import com.dropbox.client2.session.RequestTokenPair;
import com.dropbox.client2.session.Session.AccessType;
import com.dropbox.client2.session.WebAuthSession;
import com.dropbox.client2.session.WebAuthSession.WebAuthInfo;

public class DropboxWrapper extends Controller {
  
	private static final String APP_KEY = "ow4tlb9w8pr4g78";
    private static final String APP_SECRET = "x6p1q0p5vbofl6b";
    private static final AccessType ACCESS_TYPE = AccessType.DROPBOX;
    
    private static String HOST = "http://localhost:9000";
    //private static String CALL_BACK_URL = HOST + "/dropbox/return";//HAS TO include http://
    
    private static ConcurrentHashMap <String, RequestTokenPair> pairs = new ConcurrentHashMap<String, RequestTokenPair>();
        
    ///////////////////////////////////////////////////////////////////////////////////
    //Authentication methods
    ///////////////////////////////////////////////////////////////////////////////////
	public static Result link(Long id) {
				
		AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
		WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE);
		
		try {
			
			HOST = CollectorService.getBaseUrl(Services.FILE);
			
			String callBackUrl = HOST + "/dropbox/"+ id + "/return";
			
			WebAuthInfo authInfo = session.getAuthInfo(callBackUrl);

			RequestTokenPair pair = authInfo.requestTokenPair;
	        String url = authInfo.url;
	        
	        // Pair.key is returned as oauth_token in callback call
	        pairs.put(pair.key, pair);
	 
	        return (redirect(url));
	        
		} catch (DropboxException e) {
			e.printStackTrace();
			return internalServerError("Error trying to get Dropbox Authentication");
		}		
	}
	
	public static Result callBackAuth(Long id, String uid, String oauth_token){
		//Play.application().configuration().getString("you.host") + "/"
		boolean success = true;
		
		//return TODO;

		try {
			AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
			WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE);
			
			// Rebuild session
			session.retrieveWebAccessToken(pairs.get(oauth_token));
			
			// Get token
	        AccessTokenPair token = session.getAccessTokenPair();
	        saveToken(id, token);
		        
		} catch (DropboxException e) {
			success = false;
			Logger.info(e.getMessage());
			e.printStackTrace();
		} catch (Exception e){
			success = false;
			Logger.info(e.getMessage());
			e.printStackTrace();
		}
		//return ok(toJson("Dropbox account successfully linked!"));
		return redirect(controllers.routes.Filyou.linkCallback(id, success));
		
	}
	
	private static void saveToken(Long id, AccessTokenPair token) throws Exception {
		FileUser p;
		try {
			p = FileUser.find(id);
			p.dpkey = token.key;
			p.dpSecret = token.secret;
			p.update();
		} catch (Exception e){ //User does not exists
			throw new Exception(e);
		}
	}
	
	///////////////////////////////////////////////////////////////////////////////////
	//Business methods
	///////////////////////////////////////////////////////////////////////////////////

	public static Promise<List<FileInfo>> getMetadataAsync(long dropboxUserId, final String filePath) throws Exception {
		  FileUser p = FileUser.find(dropboxUserId);
		  return getMetadataAsync(p.dpkey,  p.dpSecret, filePath);
	}
	
	public static Promise<List<FileInfo>> getMetadataAsync(final String key, final String secret, final String filePath) throws Exception {
		return Akka.future(new Callable<List<FileInfo>>(){
			@Override
			public List<FileInfo> call() throws Exception {
				return getMetadata(key,secret,filePath);
			}});
	}
	
	/**
	 * Get Metadata from ROOT
	 * @param key
	 * @param secret
	 * @throws Exception
	 */
	public static List<FileInfo> getMetadata(String key, String secret, String filePath) throws Exception{
		DropboxAPI<WebAuthSession> mDBApi;
		AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
		AccessTokenPair accessTokenPair = new AccessTokenPair(key, secret);
		WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE, accessTokenPair);
		mDBApi = new DropboxAPI<WebAuthSession>(session);
		
		List<FileInfo> fileChd = null;
		
		if (filePath == null || filePath.isEmpty()){
			filePath = "";
		}
		
		//Max 1000 files. If = 0 then 25000 (dropbox default)
		int maxFiles = 1000;
		try {
			DropboxAPI.Entry fileEntry = mDBApi.metadata(filePath, maxFiles, null, true, null);
			
			if (fileEntry.isDir){
				List<DropboxAPI.Entry> children = fileEntry.contents;
				fileChd = new ArrayList<FileInfo>(children.size());
				
				for (DropboxAPI.Entry entry : children){
					fileChd.add(
							new FileInfo(entry.fileName(), entry.size, entry.isDir, entry.path, RESTUtility.parseDate(entry.modified))
							);
				}
			} else {
				fileChd = new ArrayList<FileInfo>(1);
				fileChd.add(new FileInfo(fileEntry.fileName(), fileEntry.size, fileEntry.isDir, fileEntry.path, RESTUtility.parseDate(fileEntry.modified)));
			}
			return fileChd;		
		} catch (DropboxException e) {
			throw new Exception("Error getting files data:" + e.getMessage(), e);
		}
	}

	public static Promise<List<FileInfo>> searchAsync(long dropboxUserId, final String query, final String path) throws Exception {
		  FileUser p = FileUser.find(dropboxUserId);
		  return searchAsync(p.dpkey, p.dpSecret, query, path);
	}
	
	public static Promise<List<FileInfo>> searchAsync(final String key, final String secret, final String query, final String path) throws Exception {
		return Akka.future(new Callable<List<FileInfo>>(){
			@Override
			public List<FileInfo> call() throws Exception {
				return search(key, secret, query, path);
			}});
	}
	
	public static List<FileInfo> search(String key, String secret, String query, String path) throws Exception{
		DropboxAPI<WebAuthSession> mDBApi;
		AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
		AccessTokenPair accessTokenPair = new AccessTokenPair(key, secret);
		WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE, accessTokenPair);
		mDBApi = new DropboxAPI<WebAuthSession>(session);
		
		List<FileInfo> fileChd = new ArrayList<FileInfo>();
		
		if (query == null || query.isEmpty()){
			query = "";
		}
		
		if (path == null || path.isEmpty()){
			path = "";
		}
		
		//Max 1000 files. If = 0 then 25000 (dropbox default)
		int maxFiles = 1000;
		try {
			for (DropboxAPI.Entry entry : mDBApi.search(path, query, maxFiles, false)){
				fileChd.add(
						new FileInfo(entry.fileName(), entry.size, entry.isDir, entry.path, RESTUtility.parseDate(entry.modified))
						);
			}
			return fileChd;		
		} catch (DropboxException e) {
			throw new Exception("Error getting files data:" + e.getMessage(), e);
		}
	}
	
	public static Promise<InputStream> getFileAsync(long dropboxUserId ,final String path) {
		FileUser p = FileUser.find(dropboxUserId);
		return getFileAsync(p.dpkey,p.dpSecret,path);
	}
	
	public static Promise<InputStream> getFileAsync(final String key,final String secret,final String path) {
		return Akka.future(new Callable<InputStream>(){
			@Override
			public InputStream call() throws Exception {
				return getFile(key, secret, path);
			}});
	}
	
	public static InputStream getFile(String key, String secret, String path) {
		DropboxAPI<WebAuthSession> mDBApi;
		AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
		AccessTokenPair accessTokenPair = new AccessTokenPair(key, secret);
		WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE, accessTokenPair);
		mDBApi = new DropboxAPI<WebAuthSession>(session);
		
		try {
			return mDBApi.getFileStream(path, null);
		} catch (Exception e){
			return null;
		}
		
	}
	
	///////////////////////////////////////////////////////////////////////////////////
	//Unused methods. Preserved for future possible use
	///////////////////////////////////////////////////////////////////////////////////	
	/*
	public static String upload(String key, String secret, String filename, File file) throws Exception{
		try {
			DropboxAPI<WebAuthSession> mDBApi;
			
		    FileInputStream fis = null;			
		    fis = new FileInputStream(file);
		    
			AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
			
			AccessTokenPair accessTokenPair = new AccessTokenPair(key, secret);
			
			WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE, accessTokenPair);
			
			mDBApi = new DropboxAPI<WebAuthSession>(session);
			
	        System.out.println("Uploading file...");
	        DropboxAPI.Entry newEntry = mDBApi.putFile("/files/entity/" + filename, fis, file.length(), null, null);
	        System.out.println("Uploaded!");
	        return newEntry.path;
		} catch (Exception e){
			throw new Exception (e);
		}
		
	}

	public static boolean delete(String key, String secret, String path) throws Exception{
		DropboxAPI<WebAuthSession> mDBApi;
		AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
		AccessTokenPair accessTokenPair = new AccessTokenPair(key, secret);
		WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE, accessTokenPair);
		mDBApi = new DropboxAPI<WebAuthSession>(session);
		
		mDBApi.delete(path);
		
		return false;
	} */
}